﻿#define USINGCONTEXT
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.Storage.Table.DataServices;
using POC.Azure.Model;
using System;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Linq;
using System.Text;

namespace POC.Azure.Storage.Data
{
    public class AzureTableStorageDataSource : TableStorageRespository<SessionStateEntity>
    {
        CloudStorageAccount _cloud;
        AzureTableStorageServiceContext _tableStorageServiceContext;
        public AzureTableStorageDataSource()
            : base()
        {
            InitServiceContext();
        }
        public AzureTableStorageDataSource(AzureAccount account)
            : base(account)
        {
            InitServiceContext();
        }
        public AzureTableStorageDataSource(AzureAccount account, string tableName)
            : base(account, tableName)
        {
            InitServiceContext();
        }

        void InitServiceContext()
        {
            base.InitializeRepository();
            _cloud = _account == null ? CloudStorageAccount.DevelopmentStorageAccount : _account.CloudAccount;
            _tableStorageServiceContext = new AzureTableStorageServiceContext(_client);
        }

        #region Session state CRUD operations

        public IEnumerable<SessionStateEntity> GetSessions(string sessionId, string applicationName, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;
            var results = (from c in _tableStorageServiceContext.GetSessionTable(StorageTableName)
                           where c.PartitionKey == sessionId && c.ApplicationName == applicationName
                           select c);

            var query = results.AsTableServiceQuery(_tableStorageServiceContext);
            var items = query.Execute().ToList();
            return items;
#else

            string one = TableQuery.CombineFilters(
                      TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sessionId),
                      TableOperators.And,
                      TableQuery.GenerateFilterCondition("ApplicationName", QueryComparisons.Equal, applicationName));

            string two = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.NotEqual, "");
            var three = TableQuery.CombineFilters(one, TableOperators.And, two);

            var query = new TableQuery().Where(three);

            var cools = this.GetEntries(query);
            return cools.Cast<SessionStateEntity>().ToList();
#endif
        }

        public IEnumerable<SessionStateEntity> GetSessions(string sessionId, string applicationName, int lockId, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;
            var results = from c in _tableStorageServiceContext.GetSessionTable(StorageTableName)
                          where c.PartitionKey == sessionId && c.ApplicationName == applicationName
                                && c.LockId == lockId
                          select c;

            var query = results.AsTableServiceQuery(_tableStorageServiceContext);
            var items = query.Execute().ToList();
            return items;
#else
            string one = TableQuery.CombineFilters(
                TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sessionId),
                TableOperators.And,
                TableQuery.GenerateFilterCondition("ApplicationName", QueryComparisons.Equal, applicationName));

            string two = TableQuery.CombineFilters(
                TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.NotEqual, ""),
                TableOperators.And,
                TableQuery.GenerateFilterConditionForInt("LockId", QueryComparisons.Equal, lockId));

            var three = TableQuery.CombineFilters(one, TableOperators.And, two);

            var query = new TableQuery().Where(three);

            var cools = this.GetEntries(query);
            return cools.Cast<SessionStateEntity>().ToList();
#endif
        }

        public IEnumerable<SessionStateEntity> GetSessions(string sessionId, string applicationName, DateTime expires, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;
            var results = from c in _tableStorageServiceContext.GetSessionTable(StorageTableName)
                          where c.PartitionKey == sessionId && c.ApplicationName == applicationName
                                && c.Expires > expires
                          select c;

            var query = results.AsTableServiceQuery(_tableStorageServiceContext);
            var items = query.Execute().ToList();
            return items;
#else
            string one = TableQuery.CombineFilters(
                            TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sessionId),
                            TableOperators.And,
                            TableQuery.GenerateFilterCondition("ApplicationName", QueryComparisons.Equal, applicationName));

            string two = TableQuery.CombineFilters(
                TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.NotEqual, ""),
                TableOperators.And,
                TableQuery.GenerateFilterConditionForDate("Expires", QueryComparisons.GreaterThan, expires));

            var three = TableQuery.CombineFilters(one, TableOperators.And, two);

            var query = new TableQuery().Where(three);

            var cools = this.GetEntries(query);
            return cools.Cast<SessionStateEntity>().ToList();
#endif
        }

        public IEnumerable<SessionStateEntity> GetSessions(string sessionId, string applicationName, bool isLocked, DateTime expires, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;
            var results = from c in _tableStorageServiceContext.GetSessionTable(StorageTableName)
                          where c.PartitionKey == sessionId && c.ApplicationName == applicationName
                                && c.Locked == isLocked && c.Expires > expires
                          select c;

            var query = results.AsTableServiceQuery(_tableStorageServiceContext);
            var items = query.Execute().ToList();
            return items;
#else
            string one = TableQuery.CombineFilters(
                            TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sessionId),
                            TableOperators.And,
                            TableQuery.GenerateFilterCondition("ApplicationName", QueryComparisons.Equal, applicationName));

            string two = TableQuery.CombineFilters(
                TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.NotEqual, ""),
                TableOperators.And,
                TableQuery.GenerateFilterConditionForDate("Expires", QueryComparisons.GreaterThan, expires));

            var three = TableQuery.CombineFilters(one, TableOperators.And, two);

            var query = new TableQuery().Where(three);

            var cools = this.GetEntries(query);
            return cools.Cast<SessionStateEntity>().ToList();
#endif
        }

        public void Delete(SessionStateEntity itemToDelete, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;
            _tableStorageServiceContext.Detach(itemToDelete);
            try
            {
                _tableStorageServiceContext.AttachTo(StorageTableName, itemToDelete, "*");
            }
            catch (Exception jrfjd)
            {
            }
            _tableStorageServiceContext.DeleteObject(itemToDelete);
            _tableStorageServiceContext.SaveChangesWithRetries();
#else
            DeleteEntry(itemToDelete);
#endif
        }

        public void Insert(SessionStateEntity newItem, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;
            _tableStorageServiceContext.AddObject(StorageTableName, newItem);
            _tableStorageServiceContext.SaveChangesWithRetries();
#else

            AddEntry(newItem);
#endif
        }

        public void Update(SessionStateEntity itemToUpdate, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;

            _tableStorageServiceContext.Detach(itemToUpdate);
            _tableStorageServiceContext.AttachTo(StorageTableName, itemToUpdate, "*");
            _tableStorageServiceContext.UpdateObject(itemToUpdate);
            _tableStorageServiceContext.SaveChangesWithRetries();
#else
            PutEntry(itemToUpdate);
#endif
        }

        /// <summary>
        /// Get all expired session-states
        /// TODO: This will be slow since it does a full table scan, consider adding a table indexed on expired to make this quicker
        /// </summary>
        /// <param name="applicationName">Application name</param>
        /// <param name="expires">Session-state expiration date</param>
        /// <param name="mergeOption">Client entity tracking merge option</param>
        /// <returns>Expired sessions</returns>
        public IEnumerable<SessionStateEntity> GetExpiredSessions(string applicationName, DateTime expires, MergeOption mergeOption = MergeOption.AppendOnly)
        {
#if USINGCONTEXT
            _tableStorageServiceContext.MergeOption = mergeOption;
            var results = from c in _tableStorageServiceContext.GetSessionTable(StorageTableName)
                          where c.ApplicationName == applicationName
                                && c.Expires <= expires
                          select c;

            var query = results.AsTableServiceQuery(_tableStorageServiceContext);
            var items = query.Execute().ToList();
            return items;
#else
            string one = TableQuery.CombineFilters(
                    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.NotEqual, ""),
                    TableOperators.And,
                    TableQuery.GenerateFilterCondition("ApplicationName", QueryComparisons.Equal, applicationName));

            string two = TableQuery.GenerateFilterConditionForDate("Expires", QueryComparisons.LessThan, expires);

            var three = TableQuery.CombineFilters(one, TableOperators.And, two);

            var query = new TableQuery().Where(three);

            var cools = this.GetEntries(query);
            return cools.Cast<SessionStateEntity>().ToList();
#endif
        }
        #endregion

    }
}
